package com.skyon.rule.res.spring;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.drools.runtime.StatefulKnowledgeSession;
import org.drools.runtime.StatelessKnowledgeSession;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.core.io.Resource;

import com.skyon.rule.res.PlotConfiguration;
import com.skyon.rule.res.exceptions.BaseRuleException;
import com.skyon.rule.res.exceptions.InvalidRuleSourceException;
import com.skyon.rule.res.exceptions.PlotBuilderException;
import com.skyon.rule.res.model.ErrorMsg;
import com.skyon.rule.res.model.InputParam;

/**
 * Spring 加载入口
 * 提供系统初始化,得到周围环境中的各个配置信息
 * 为Configuration类提供周围环境变量信息
 * 调用Configuration的newSessionFactory， 提供并对外提供SessionFactory
 * 
 * 
 * @author cfans
 *
 */

public class LocalSessionFactoryBean extends AbstractPlotSessionBeanFactory
		implements BeanClassLoaderAware, LocalSessionFactory, RemoteHanding {

	
	private static final Map<String,ThreadLocal<StatelessKnowledgeSession>> statelessSessionHolderMap = new HashMap<String,ThreadLocal<StatelessKnowledgeSession>>();
	private static final Map<String,ThreadLocal<StatefulKnowledgeSession>>  statefulSessionHolderMap = new  HashMap<String,ThreadLocal<StatefulKnowledgeSession>>();
	
	
	private List<String>                                        plotIds = new ArrayList<String>();
	private Class<? extends PlotConfiguration>                  configurationClass = PlotConfiguration.class;
	private Map<String, PlotConfiguration>                      configurations = new HashMap<String, PlotConfiguration>();
    
	private Set<String>                                         runtimeBeans = new HashSet<String>();
    public Map<String,List<String>>                             allPlotWithProcesses = new HashMap<String,List<String>>(); 
    public Map<String,List<String>>                             allPlotRuntimeClz = new HashMap<String,List<String>>(); 
	
	
	private Resource[]                                          mappingJarLocations;
	
	@Override
	public void setBeanClassLoader(ClassLoader arg0) {
		// TODO Auto-generated method stub
		
	}

	@Override
	protected LocalSessionFactory buildSessionFactory() throws Exception {

		//TODO 向conf提供jar drl等数据
		try {
			if (this.mappingJarLocations != null) {
				// Register given mapping definitions, contained in jar files.
				for (Resource resource : this.mappingJarLocations) {
					postProcessConfiguration(resource);
				}
			}
			
		} catch (Exception e){
			e.printStackTrace();
			throw new PlotBuilderException("Could not set configuration form mapping",e);
		}
		return this;
	}
	

	protected PlotConfiguration newConfiguration() throws PlotBuilderException {
		return BeanUtils.instantiateClass(this.configurationClass);
	}

	/**
	 * @param resource 
	 * 
	 */
	protected void postProcessConfiguration(Resource resource) throws BaseRuleException {
		PlotConfiguration config = newConfiguration();

		try {
			config.addJar(resource.getFile());
		} catch (IOException e) {
			e.printStackTrace();
			throw new InvalidRuleSourceException(
					"jar", "", e
					);
		}
		config.builderkBase();
		logger.info("Building PlotServer Succeed!");
		
		for (String beanName : config.getRuntimeClasses().keySet()) {
			if (runtimeBeans.contains(beanName)) {
				throw new PlotBuilderException("duplicate invoke class name found in one context",new Exception());
			}
		}
		this.runtimeBeans.addAll(config.getRuntimeClasses().keySet());
		this.configurations.put(config.getRoot(),config);
		this.plotIds.add(config.getRoot());
		
		this.allPlotWithProcesses.put(config.getRoot(), config.getFlowURLs());
		this.allPlotRuntimeClz.put(config.getRoot(), config.getRuntimeClassNames());
		
		statelessSessionHolderMap.put(config.getRoot(),new ThreadLocal<StatelessKnowledgeSession>());
		statefulSessionHolderMap.put(config.getRoot(),new ThreadLocal<StatefulKnowledgeSession>());
	}
	
	@Override
	public ErrorMsg valid(InputParam ipm) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public StatelessKnowledgeSession getLocalStatelessSession(String id) throws PlotBuilderException {
		//ThreadLocal here
		StatelessKnowledgeSession slkSession = statelessSessionHolderMap.get(id).get();
		try {
			if (slkSession == null) {  
				slkSession = configurations.get(id).newStatelessSession();
				statelessSessionHolderMap.get(id).set(slkSession); 
			}
		} catch (Exception e) {
			e.printStackTrace();
			throw new PlotBuilderException("Could not get local session in context",e);
		}
		return slkSession;

	}

	@Override
	public StatefulKnowledgeSession getLocalStatefulSession(String id) throws PlotBuilderException {
		StatefulKnowledgeSession sfkSession = statefulSessionHolderMap.get(id).get();
		try {
			if (sfkSession == null) {  
				sfkSession = configurations.get(id).newStatefulSession();
				statefulSessionHolderMap.get(id).set(sfkSession); 
			}
		} catch (Exception e) {
			e.printStackTrace();
			throw new PlotBuilderException("Could not get local session in context",e);
		}
		return sfkSession;
	}

	@Override
	public Map<String,Class<?>> getRutimeClasses(String id) throws PlotBuilderException {
		return this.configurations.get(id).getRuntimeClasses();
	}


	/**
	 * Set locations of jar files that contain mapping resources,
	 * like "WEB-INF/lib/example.jar".
	 * @param mappingJarLocations
	 */
	public void setMappingJarLocations(Resource[] mappingJarLocations) {
		this.mappingJarLocations = mappingJarLocations;
	}

	@Override
	public boolean isPlotExist(String root) {
		logger.debug("isPlotExist root is " + root);
		for (String plotid : this.plotIds) {
			logger.debug("plotId in the SessionFactory is " + plotid);
			if (root.equals(plotid)) return true;
		}
		return false;
	}

	@Override
	public boolean handingNow() {
		return true;
	}

	@Override
	public Map<String, List<String>> getAllPlotWithProcesses() {
		return this.allPlotWithProcesses;
	}

	@Override
	public Map<String, List<String>> getAllPlotRuntimeClz() {
		return this.allPlotRuntimeClz;
	}

	public List<String> getPlotIds() {
		return plotIds;
	}

	public void setPlotIds(List<String> plotIds) {
		this.plotIds = plotIds;
	}
}
